package com.ease.component.controls {
   import com.ease.component.constant.StringConstant;
   import com.ease.component.controls.transitionclass.HideChildren;
   import com.ease.component.util.Util;

   import flash.events.Event;
   import flash.events.MouseEvent;
   import mx.containers.BoxDirection;
   import mx.containers.Canvas;
   import mx.containers.HBox;
   import mx.containers.VBox;
   import mx.controls.Label;
   import mx.controls.LinkButton;
   import mx.core.ScrollPolicy;
   import mx.core.UIComponent;
   import mx.effects.Move;
   import mx.effects.Parallel;
   import mx.effects.Resize;
   import mx.events.EffectEvent;
   import mx.events.StateChangeEvent;
   import mx.states.AddChild;
   import mx.states.IOverride;
   import mx.states.SetProperty;
   import mx.states.State;
   import mx.states.Transition;

   [Style(name = "titleBarStyleName", type = "String", inherit = "no")]

   [Style(name = "collapseArrowStyleName", type = "String", inherit = "no")]

   [Style(name = "expandBodyStyleName", type = "String", inherit = "no")]

   [Style(name = "collapseBodyStyleName", type = "String", inherit = "no")]

   [Style(name = "collapseDuration", type = "Number", format = "Time", inherit = "no")]

   [Style(name = "expandArrowStyleName", type = "String", inherit = "no")]

   [Style(name = "expandDuration", type = "Number", format = "Time", inherit = "no")]

   [Exclude(name = "setCurrentState", kind = "method")]

   public class FieldSet extends Canvas {
      public static const STATE_COLLAPSED:String = "collapsed";
      public static const STATE_EXPANDED:String = "expanded";

      private static const DEFAULT_ARROW_THICKNESS:Number = 18;
      private static const DEFAULT_COLLAPSED_HEIGHT:Number = 35;

      private static const BASE_STATE:String = STATE_EXPANDED;

      private var _titleLabel:Label;
      private var _titleBar:HBox;

      public var closable:Boolean = true;

      private var _title:String;
      private var _titleChange:Boolean;
      public function get title():String {
         return _title;
      }
      public function set title(value:String):void {
         _title = value;
         _titleChange = true;
         invalidateProperties();
      }

      private var _pos:int;
      public function get pos():int {
         return _pos;
      }
      public function set pos(value:int):void {
         _pos = value;
      }

      public function FieldSet() {
         super();
         this.collapsedHeight = DEFAULT_COLLAPSED_HEIGHT;
      }

      public var arrowButton:LinkButton;

      private var _collapseBody:VBox;
      private var _collapsedState:State;
      private var _expandBody:VBox;
      private var _expandTransition:Transition;
      private var _collapseTransition:Transition;
      private var _inTween:Boolean;
      private var _tempPercentHeight:Number;
      private var _tempHorizontalScrollPolicy:String;
      private var _tempVerticalScrollPolicy:String;

      private var _arrowButtonStyleChanged:Boolean;
      private var _collapseStyleChanged:Boolean;
      private var _expandStyleChanged:Boolean;
      private var _collapseBodyStyleChanged:Boolean;

      private var _client:*;
      private var _clientChanged:Boolean;
      public function set clientArea(value:*):void {
         if (_client != value) {
            _client = value;
            _clientChanged = true;

            invalidateProperties();
         }
      }

      private var _expandedHeight:Number;
      private var _expandedHeightChanged:Boolean = false;
      [Bindable(event = "ExpandedWidthChange")]
      public function get expandedHeight():Number {
         return _expandedHeight;
      }
      private function set expandedHeight(value:Number):void {
      }

      private var _collapsedHeight:Number = DEFAULT_COLLAPSED_HEIGHT;
      private var _collapsedHeightChanged:Boolean = false;
      [Bindable]
      public function get collapsedHeight():Number {
         return _collapsedHeight;
      }
      public function set collapsedHeight(value:Number):void {
         if (value == _collapsedHeight || value >= _collapsedHeight) {
            return;
         }

         _collapsedHeight = value;
         _collapsedHeightChanged = true;
         invalidateProperties();
      }

      private var _arrowButtonSizeChanged:Boolean = false;
      private var _arrowButtonWidth:Number = DEFAULT_ARROW_THICKNESS;
      public function get arrowButtonWidth():Number {
         return _arrowButtonWidth;
      }
      public function set arrowButtonWidth(value:Number):void {
         if (value == _arrowButtonWidth || value < 0) {
            return;
         }

         _arrowButtonWidth = value;
         _arrowButtonSizeChanged = true;
         invalidateProperties();
      }

      private var _arrowButtonHeight:Number = DEFAULT_ARROW_THICKNESS;
      public function get arrowButtonHeight():Number {
         return _arrowButtonHeight;
      }
      public function set arrowButtonHeight(value:Number):void {
         if (value == _arrowButtonHeight || value < 0) {
            return;
         }

         _arrowButtonHeight = value;
         _arrowButtonSizeChanged = true;
         invalidateProperties();
      }

      private var _hideChildrenWhileTween:Boolean;
      private var _hideChildrenChanged:Boolean = false;
      [Inspectable(type = "Boolean", enumeration = "true,false", defaultValue = "flase")]
      public function get hideChildrenWhileTween():Boolean {
         return _hideChildrenWhileTween;
      }

      public function set hideChildrenWhileTween(value:Boolean):void {
         if (value == _hideChildrenWhileTween) {
            return;
         }

         _hideChildrenWhileTween = value;
         _hideChildrenChanged = true;
         invalidateProperties();
      }

      private var _hideScrollBarsWhileTween:Boolean = true;
      private var _hideScrollBarsChanged:Boolean = false;
      [Inspectable(type = "Boolean", enumeration = "false,true", defaultValue = "true")]
      public function get hideScrollBarsWhileTween():Boolean {
         return _hideScrollBarsWhileTween;
      }
      public function set hideScrollBarsWhileTween(value:Boolean):void {
         if (value == _hideScrollBarsWhileTween) {
            return;
         }

         _hideScrollBarsWhileTween = value;
         _hideScrollBarsChanged = true;
         invalidateProperties();
      }

      override protected function createChildren():void {
         super.createChildren();

         _titleBar = new HBox();
         _titleBar.styleName = this.getStyle("titleBarStyleName");
         addChild(_titleBar);

         _expandBody = new VBox();
         _expandBody.percentWidth = 100;
         _expandBody.percentHeight = 100;
         _expandBody.x = 10;
         _expandBody.y = 20;
         _expandBody.horizontalScrollPolicy = ScrollPolicy.OFF;
         _expandBody.verticalScrollPolicy = ScrollPolicy.OFF;
         _expandBody.styleName = this.getStyle("expandBodyStyleName");
         addChild(_expandBody);

         if (closable) {
            _collapseBody = new VBox();
            _collapseBody.percentWidth = 100;
            _collapseBody.percentHeight = 100;
            _collapseBody.x = 10;
            _collapseBody.y = 20;
            _collapseBody.horizontalScrollPolicy = ScrollPolicy.OFF;
            _collapseBody.verticalScrollPolicy = ScrollPolicy.OFF;
            _collapseBody.styleName = this.getStyle("collapseBodyStyleName");

            arrowButton = new LinkButton();

            // Test automation id.
            arrowButton.id = "arrowButton";
            arrowButton.width = _arrowButtonWidth;
            arrowButton.height = _arrowButtonHeight;
            arrowButton.styleName = this.getStyle("expandArrowStyleName");
            arrowButton.toolTip = Util.string(StringConstant.FIELDSET_COLLAPSE);
            this._titleBar.addChild(arrowButton);

            _tempPercentHeight = this.percentHeight;

            createCollapsedState();
            createTransitions();
            addListeners();
         }
         
         _titleLabel = new Label();
         _titleLabel.text = title;
         _titleBar.addChild(_titleLabel);
         
         if (this.height > 0) {
            updateExpandedHeight(this.height);
         }
      }

      private function createCollapsedState():void {
         var property:SetProperty;

         _collapsedState = new State();
         _collapsedState.name = STATE_COLLAPSED;
         this.states = [_collapsedState];

         _collapsedState.overrides = [];

         var expandArrowToolTip:String = Util.string(StringConstant.FIELDSET_EXPAND);
         property = new SetProperty(arrowButton, "toolTip", expandArrowToolTip);
         _collapsedState.overrides.push(property);

         var removeChildren:IOverride = new HideChildren(_expandBody);
         _collapsedState.overrides.push(removeChildren);

         var addCollapseBody:AddChild = new AddChild();
         addCollapseBody.target = _collapseBody;
         _collapsedState.overrides.push(addCollapseBody);
      }

      private function createTransitions():void {
         _expandTransition = new Transition();
         _collapseTransition = new Transition();

         _expandTransition.fromState = getValidStateName(STATE_COLLAPSED);
         _expandTransition.toState = getValidStateName(STATE_EXPANDED);
         buildExpandTransition();

         _collapseTransition.fromState = getValidStateName(STATE_EXPANDED);
         _collapseTransition.toState = getValidStateName(STATE_COLLAPSED);
         buildCollapseTransition();

         this.transitions = [_expandTransition, _collapseTransition];
      }

      private function buildExpandTransition():void {
         var transition:Parallel = new Parallel();
         var expandDuration:Number = this.getStyle("expandDuration");
         var easingFunction:Function = this.getStyle("expandEasingFunction") as Function;

         // Transition the size.
         var resize:Resize = new Resize(this);
         resize.heightFrom = _collapsedHeight;
         resize.heightTo = _expandedHeight;
         resize.duration = expandDuration;
         resize.easingFunction = easingFunction;

         if (_hideChildrenWhileTween) {
            resize.hideChildrenTargets = [_expandBody];
         }

         transition.addChild(resize);
         transition.addEventListener(EffectEvent.EFFECT_START, onTransitionStart,
            false, 0, true);
         transition.addEventListener(EffectEvent.EFFECT_END, onTransitionEnd,
            false, 0, true);
         _expandTransition.effect = transition;
      }

     private function buildCollapseTransition():void {
        var transition:Parallel = new Parallel();
        var collapseDuration:Number = this.getStyle("collapseDuration");
        var easingFunction:Function = this.getStyle("collapseEasingFunction") as Function;

        // Transition the size.
        var resize:Resize = new Resize(this);
        resize.heightFrom = _expandedHeight;
        resize.heightTo = _collapsedHeight;
        resize.duration = collapseDuration;
        resize.easingFunction = easingFunction;

        if (_hideChildrenWhileTween) {
           resize.hideChildrenTargets = [_expandBody];
        }

        transition.addChild(resize);
        transition.addEventListener(EffectEvent.EFFECT_START, onTransitionStart,
           false, 0, true);
        transition.addEventListener(EffectEvent.EFFECT_END, onTransitionEnd,
           false, 0, true);
        _collapseTransition.effect = transition;
     }

      private function updateExpandedHeight(value:Number):void {
         if (_expandedHeight == value || value <= _expandedHeight) {
            return;
         }

         _expandedHeight = value;
         _expandedHeightChanged = true;
         invalidateProperties();
         this.dispatchEvent(new Event("ExpandedWidthChange"));
      }

      private function addListeners():void {
         arrowButton.addEventListener(MouseEvent.CLICK, onArrowButtonClick, false,
            0, true);
         this.addEventListener(StateChangeEvent.CURRENT_STATE_CHANGING, onStateChanging,
            false, 0, true);
         this._titleBar.doubleClickEnabled = true;
         this._titleBar.addEventListener(MouseEvent.DOUBLE_CLICK, onDoubleClick,
            false, 0, true);
      }
      
      private function onArrowButtonClick(event:MouseEvent):void {
         toggleCurrentState();
      }

      private function onDoubleClick(event:MouseEvent):void {
         if (event.target == this._titleBar) {
            toggleCurrentState();
         }
      }

      private function onStateChanging(event:StateChangeEvent):void {
         _inTween = true;

         if (event.newState == getValidStateName(STATE_COLLAPSED)) {
            _tempPercentHeight = this.percentHeight;
         }
      }

      override protected function commitProperties():void {
         super.commitProperties();

         if (closable && (_expandedHeightChanged || _collapsedHeightChanged)) {
            buildExpandTransition();
            buildCollapseTransition();
            _expandedHeightChanged = false;
         }

         if (closable && _collapsedHeightChanged) {
            if (currentState == STATE_COLLAPSED && this.initialized && !_inTween) {
               this.setActualSize(_collapsedHeight, this.getExplicitOrMeasuredHeight());
            }

            _collapsedHeightChanged = false;
         }

         if (closable && _arrowButtonSizeChanged) {
            arrowButton.setActualSize(_arrowButtonWidth, _arrowButtonHeight);
            _arrowButtonSizeChanged = false;
         }

         if (closable && _hideChildrenChanged) {
            buildExpandTransition();
            buildCollapseTransition();
            _hideChildrenChanged = false;
         }

         if (_hideScrollBarsChanged && !_inTween) {
            this.horizontalScrollPolicy = _tempHorizontalScrollPolicy;
            this.verticalScrollPolicy = _tempVerticalScrollPolicy;
            _hideScrollBarsChanged = false;
         }

         if (_clientChanged) {
            _expandBody.removeAllChildren();

            if (_client is UIComponent) {
               if (!_expandBody.contains(_client)) {
                  _expandBody.addChild(_client);
               }
            } else {
               for each (var item:* in _client) {
                  if (!_expandBody.contains(item)) {
                     _expandBody.addChild(item);
                  }
               }
            }
            _clientChanged = false;
         }

         if (_titleChange) {
            _titleLabel.text = _title;
            _titleChange = false;
         }
      }

      override public function styleChanged(styleProperty:String):void {
         var allStyles:Boolean = (styleProperty == null || styleProperty == "styleName");
         super.styleChanged(styleProperty);

         if (allStyles || styleProperty == "collapseDuration" ||
            styleProperty == "collapseEasingFunction") {
            _collapseStyleChanged = true;
            invalidateProperties();
         }

         if (allStyles || styleProperty == "expandDuration" ||
            styleProperty == "expandEasingFunction") {
            _expandStyleChanged = true;
            invalidateProperties();
         }

         if (allStyles || styleProperty == "expandArrowStyleName" ||
            styleProperty == "collapseArrowStyleName") {
            _arrowButtonStyleChanged = true;
            invalidateProperties();
         }

         if (allStyles || styleProperty == "collapseBodyStyleName") {
            _collapseBodyStyleChanged = true;
            invalidateProperties();
         }
      }

      override public function setCurrentState(stateName:String,
                                               playTransition:Boolean = true):void {
         stateName = getValidStateName(stateName);
         super.setCurrentState(stateName, playTransition);
      }

      override protected function measure():void {
         super.measure();
         measuredWidth += 20;
         measuredHeight += 10;
      }

      override protected function updateDisplayList(unscaledWidth:Number, unscaledHeight:Number):void {
         if (!_inTween) {
            if (currentState != STATE_COLLAPSED || isNaN(_expandedHeight)) {
               updateExpandedHeight(unscaledHeight);
            }

            if (currentState == STATE_COLLAPSED && unscaledHeight != _collapsedHeight) {
               unscaledHeight = _collapsedHeight;

               if (isNaN(_tempPercentHeight)) {
                  this.explicitHeight = _collapsedHeight;
               }
            }
         }

         super.updateDisplayList(unscaledWidth, unscaledHeight);
         updateArrowPosition();
         graphics.clear();

         pos = pos <= 0 ? 18 : pos;
         _titleBar.move(pos, 0);

         var borderColor:uint = getStyle("borderColor") >= 0 ? getStyle("borderColor") : 0x000000;
         var borderAlpha:Number = getStyle("borderAlpha") >= 0 ? getStyle("borderAlpha") : 1;
         graphics.lineStyle(1, borderColor, borderAlpha);
         graphics.moveTo(_titleBar.x - 2, _titleBar.height/2);
         graphics.lineTo(0, _titleBar.height/2);
         graphics.lineTo(0, unscaledHeight);
         graphics.lineTo(unscaledWidth, unscaledHeight);
         graphics.lineTo(unscaledWidth, _titleBar.height/2);
         graphics.lineTo(_titleBar.x + _titleBar.width + 2, _titleBar.height/2);
         
      }

      private function getValidStateName(stateName:String):String {
         var valideState:String;

         if (stateName == STATE_COLLAPSED || stateName == STATE_EXPANDED) {
            valideState = stateName;
         }

         if (stateName == BASE_STATE) {
            valideState = "";
         }

         return valideState;
      }

      private function onTransitionStart(event:EffectEvent):void {
         if (_hideScrollBarsWhileTween) {
            _tempHorizontalScrollPolicy = this.horizontalScrollPolicy;
            _tempVerticalScrollPolicy = this.verticalScrollPolicy;
            this.horizontalScrollPolicy = ScrollPolicy.OFF;
            this.verticalScrollPolicy = ScrollPolicy.OFF;
         }

         arrowButton.enabled = false;
      }

      private function onTransitionEnd(event:EffectEvent):void {
         if (_hideScrollBarsWhileTween) {
            this.horizontalScrollPolicy = _tempHorizontalScrollPolicy;
            this.verticalScrollPolicy = _tempVerticalScrollPolicy;
         }

         arrowButton.enabled = true;
         _inTween = false;
         updateArrowPosition();

         if (currentState == null && !isNaN(_tempPercentHeight)) {
            this.percentHeight = _tempPercentHeight;
         }
      }

      private function updateArrowPosition():void {
         if (closable) {
            updateArrowButtonStyle();
         }
      }

      private function updateArrowButtonStyle():void {
         var styleName:String;
         var expandStyle:String = "expandArrowStyleName";
         var collapseStyle:String = "collapseArrowStyleName";

         styleName = (currentState == null) ? expandStyle : collapseStyle;
         arrowButton.styleName = this.getStyle(styleName);
      }

      private function toggleCurrentState():void {
         currentState =
            (currentState == null) ? STATE_COLLAPSED : STATE_EXPANDED;
      }
   }
}